<!DOCTYPE html> <!-- -*- html -*- -->
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1, maximum-scale=1">
  <title>Matrix Dynamics</title>
  <link rel="shortcut icon" href="img/gatech.gif"/>

  
      <link rel="stylesheet" href="css/demo.css?vers=73bcc3">
  

  <style>
      

html, body {
    margin:           0;
    height:           100%;
    background-color: white;
    overflow-x:       hidden;
}
#mathbox {
    width:       100%;
    height:      100%;
}
.espace-1 {
    color: var(--palette-green);
}
.espace-2 {
    color: var(--palette-violet);
}
.espace-3 {
    color: var(--palette-brown);
}
.overlay-text {
    z-index: 1;
}
#gui-container {
  position: absolute;
  right:    10px;
  top:      0px;
  z-index:  2;
}
#mult-here.hidden {
    display: none;
}

  </style>

</head>
<body>
    
<div id="caption" class="overlay-text"></div>
<div id="gui-container"></div>
<div id="mathbox"></div>


    
    
        <script src="js/demo.js?vers=77646a"></script>
    
    <script src="js/dynamics.js?vers=a4ed0b"></script>


    <script type="text/javascript">
        "use strict";
        DomReady.ready(function() {

        var almostZero, axisColors, basis1, basis2, controller, demo, duration, dynView, dynamicsDemo, eigenStr, eigenStrs, eigenz, gui, i, len, matName, matrix, matrixInfo, mv3v2, normalize, params, ref, ref1, ref2, ref3, row, size, testMatrices, type, typeOpts, v1, v1p, v2, v2p, v3, v3p, vecName, vectorIn;

testMatrices = {
  Shear: [[2 / 3, 1 / 3], [-1 / 3, 4 / 3]],
  ScaleOutShear: [[5 / 3, 1 / 3], [-1 / 3, 7 / 3]],
  ScaleInShear: [[1 / 3, 1 / 6], [-1 / 6, 2 / 3]],
  Circle: [[1 / 3, -1 / 3], [5 / 6, 2 / 3]],
  SpiralIn: [[1 / 3, -1 / 3], [5 / 6, 2 / 3]],
  SpiralOut: [[2 / 3, -2 / 3], [5 / 3, 4 / 3]],
  AttractLine: [[5 / 6, 1 / 6], [1 / 3, 2 / 3]],
  RepelLine: [[4 / 3, -1 / 3], [-2 / 3, 5 / 3]],
  Attract: [[7 / 18, -1 / 18], [-1 / 9, 4 / 9]],
  Repel: [[8 / 3, 1 / 3], [2 / 3, 7 / 3]],
  Hyperbolas: [[8 / 9, -5 / 9], [-10 / 9, 13 / 9]]
};

ref = testMatrices.Circle;
for (i = 0, len = ref.length; i < len; i++) {
  row = ref[i];
  row[0] *= Math.sqrt(2);
  row[1] *= Math.sqrt(2);
}

almostZero = function(x) {
  return Math.abs(x) < 1e-5;
};

normalize = function(b) {
  var n;
  n = Math.sqrt(b[0] * b[0] + b[1] * b[1]);
  return [b[0] / n, b[1] / n];
};

mv3v2 = function(v1, v2, b) {
  return [v1[0] * b[0] + v2[0] * b[1], v1[1] * b[0] + v2[1] * b[1], v1[2] * b[0] + v2[2] * b[1]];
};

matrixInfo = function(matrix) {
  var Imλ, Reλ, a, axisColors, b, basis1, basis2, c, d, determinant, discriminant, eigenStrs, negate1, negate2, opts, ref1, ref2, ref3, ref4, str, swap, trace, type, v, δ, λ, λ1, λ1p, λ2, λ2p;
  (ref1 = matrix[0], a = ref1[0], b = ref1[1]), (ref2 = matrix[1], c = ref2[0], d = ref2[1]);
  trace = a + d;
  determinant = a * d - b * c;
  discriminant = trace * trace - 4 * determinant;
  basis1 = [];
  basis2 = [];
  type = null;
  opts = {};
  axisColors = [new Color("green").arr(1), new Color("violet").arr(1), new Color("brown").arr(1)];
  eigenStrs = [];
  if (almostZero(determinant)) {
    throw "Can't handle non-invertible matrix";
  }
  if (almostZero(discriminant)) {
    λ = trace / 2;
    if (λ < 0) {
      throw "Can't handle negative real eigenvalues";
    }
    if (almostZero(b) && almostZero(c)) {
      basis1 = [1, 0];
      basis2 = [0, 1];
      axisColors[1] = axisColors[0];
      eigenStrs.push("The whole plane is the <span class=\"espace-1\">" + ((λ.toFixed(2)) + "-eigenspace.</span>"));
      type = λ > 1 ? dynamics.Repel : dynamics.Attract;
      opts = {
        λ1: λ,
        λ2: λ
      };
    } else {
      if (almostZero(b)) {
        basis1 = normalize([λ - d, c]);
      } else {
        basis1 = normalize([b, λ - a]);
      }
      basis2 = [basis1[1], -basis1[0]];
      axisColors[1] = [0.5, 0.5, 0.5, 0.3];
      eigenStrs.push("This is the <span class=\"espace-1\">" + ((λ.toFixed(2)) + "-eigenspace.</span>"));
      v = [(a - λ) * basis2[0] + b * basis2[1], c * basis2[0] + (d - λ) * basis2[1]];
      if (almostZero(basis1[0])) {
        opts.translate = v[1] / (λ * basis1[1]);
      } else {
        opts.translate = v[0] / (λ * basis1[0]);
      }
      if (almostZero(λ - 1)) {
        type = dynamics.Shear;
      } else {
        if (λ > 1) {
          type = dynamics.ScaleOutShear;
        } else {
          type = dynamics.ScaleInShear;
        }
        opts.scale = λ;
      }
    }
  } else if (discriminant < 0) {
    Reλ = trace / 2;
    Imλ = Math.sqrt(-discriminant) / 2;
    basis1 = [b / Imλ, (Reλ - a) / Imλ];
    basis2 = [0, 1];
    axisColors[0] = [0.5, 0.5, 0.5, 0.3];
    axisColors[1] = [0.5, 0.5, 0.5, 0.3];
    str = 'This matrix has complex eigenvalues ';
    str += katex.renderToString("\\lambda = " + (Reλ.toFixed(2)) + "\\pm " + (Imλ.toFixed(2)) + "i.");
    eigenStrs.push(str);
    opts = {
      θ: Math.atan2(-Imλ, Reλ),
      scale: Math.sqrt(determinant),
      dist: 'cont'
    };
    if (almostZero(opts.scale - 1)) {
      type = dynamics.Circle;
    } else {
      if (opts.scale > 1) {
        type = dynamics.SpiralOut;
      } else {
        type = dynamics.SpiralIn;
      }
    }
  } else if (discriminant > 0) {
    δ = Math.sqrt(discriminant);
    λ1 = (trace - δ) / 2;
    λ2 = (trace + δ) / 2;
    negate1 = false;
    negate2 = false;
    if (λ1 < 0) {
      λ1 *= -1;
      negate1 = true;
    }
    if (λ2 < 0) {
      λ2 *= -1;
      negate2 = true;
    }
    if (λ1 >= λ2) {
      ref3 = [λ1, λ2], λ2 = ref3[0], λ1 = ref3[1];
      ref4 = [negate1, negate2], negate2 = ref4[0], negate1 = ref4[1];
    }
    opts = {
      λ1: λ1,
      λ2: λ2,
      negate1: negate1,
      negate2: negate2
    };
    if (almostZero(b)) {
      if (almostZero(c)) {
        if (almostZero(a - λ1)) {
          basis1 = [1, 0];
          basis2 = [0, 1];
        } else {
          basis2 = [1, 0];
          basis1 = [0, 1];
        }
      } else {
        basis1 = normalize([λ1 - d, c]);
        basis2 = normalize([λ2 - d, c]);
      }
    } else {
      basis1 = normalize([b, λ1 - a]);
      basis2 = normalize([b, λ2 - a]);
    }
    swap = function() {
      var ref5, ref6, ref7;
      ref5 = [opts.negate1, opts.negate2], opts.negate2 = ref5[0], opts.negate1 = ref5[1];
      ref6 = [opts.λ1, opts.λ2], opts.λ2 = ref6[0], opts.λ1 = ref6[1];
      return ref7 = [basis1, basis2], basis2 = ref7[0], basis1 = ref7[1], ref7;
    };
    if (almostZero(λ1 - 1)) {
      type = dynamics.RepelLine;
    } else if (almostZero(λ2 - 1)) {
      type = dynamics.AttractLine;
      swap();
    } else if (λ1 < 1 && λ2 < 1) {
      type = dynamics.Attract;
      swap();
    } else if (λ1 < 1 && λ2 > 1) {
      type = dynamics.Hyperbolas;
    } else if (λ1 > 1 && λ2 > 1) {
      type = dynamics.Repel;
    }
    λ1p = opts.λ1 * (opts.negate1 ? -1 : 1);
    λ2p = opts.λ2 * (opts.negate2 ? -1 : 1);
    eigenStrs.push("This is the <span class=\"espace-1\">" + ((λ1p.toFixed(2)) + "-eigenspace.</span>"));
    eigenStrs.push("This is the <span class=\"espace-2\">" + ((λ2p.toFixed(2)) + "-eigenspace.</span>"));
  }
  return {
    basis1: basis1,
    basis2: basis2,
    type: type,
    typeOpts: opts,
    axisColors: axisColors,
    eigenStrs: eigenStrs
  };
};

dynamicsDemo = function(controller, opts) {
  var captionElt, demo, dragHook, dynView, eigenStr, eigenz, element, matName, params, ref1, ref10, ref11, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, size, suffix, vecColor, vecName, vectorIn, vectorOut;
  dynView = opts.dynView;
  params = opts.params;
  eigenStr = opts.eigenStr;
  matName = (ref1 = opts.matName) != null ? ref1 : 'A';
  vecName = (ref2 = opts.vecName) != null ? ref2 : 'x';
  size = (ref3 = opts.size) != null ? ref3 : 2;
  eigenz = (ref4 = opts.eigenz) != null ? ref4 : 1;
  suffix = (ref5 = opts.suffix) != null ? ref5 : '';
  element = (ref6 = opts.element) != null ? ref6 : document.getElementById("mathbox");
  captionElt = (ref7 = opts.captionElt) != null ? ref7 : document.getElementById("caption");
  vecColor = (ref8 = opts.vecColor) != null ? ref8 : new Color("red");
  dragHook = (ref9 = opts.dragHook) != null ? ref9 : function() {};
  vectorIn = (ref10 = (ref11 = opts.vectorIn) != null ? ref11.slice() : void 0) != null ? ref10 : [.5, 0, 0];
  vectorOut = [0, 0, 0];
  return demo = window.demo = new (size === 3 ? Demo : Demo2D)({
    mathbox: {
      element: element,
      size: {
        aspect: 1
      }
    },
    vertical: 1.05
  }, function() {
    var clipCube, computeOut, computePath, diff, displayMat, drag, labeled, matElt, multElt, path, pathElt, snap, snapped, str, subspace, te, updateCaption, view, viewT;
    view = this.view({
      viewRange: [[-1, 1], [-1, 1], [-1, 1]],
      axes: false,
      grid: false
    });
    clipCube = this.clipCube(view, {
      draw: size === 3,
      hilite: false
    });
    dynView.updateView(this.mathbox, clipCube.clipped);
    this.dynView = dynView;
    this.controller = controller;
    te = dynView.matrixOrigCoords.elements;
    if (size === 2) {
      displayMat = [[te[0], te[1]], [te[4], te[5]]];
    } else if (size === 3) {
      displayMat = [[te[0], te[1], te[2]], [te[4], te[5], te[6]], [te[8], te[9], te[10]]];
    }
    str = ("<p><span id=\"mat-here" + suffix + "\"></span>") + ("<span id=\"mult-here" + suffix + "\"></span></p>");
    str += eigenStr;
    captionElt.innerHTML = str;
    matElt = document.getElementById("mat-here" + suffix);
    multElt = document.getElementById("mult-here" + suffix);
    str = (matName + " = ") + this.texMatrix(displayMat);
    katex.render(str, matElt);
    updateCaption = (function(_this) {
      return function() {
        var vin, vout, x;
        vin = vectorIn.slice(0, size);
        vout = vectorOut.slice(0, size);
        str = '\\qquad ' + matName + _this.texVector((function() {
          var j, len1, results;
          results = [];
          for (j = 0, len1 = vin.length; j < len1; j++) {
            x = vin[j];
            results.push(x * 10);
          }
          return results;
        })(), {
          color: vecColor
        }) + " = " + _this.texVector((function() {
          var j, len1, results;
          results = [];
          for (j = 0, len1 = vout.length; j < len1; j++) {
            x = vout[j];
            results.push(x * 10);
          }
          return results;
        })(), {
          color: vecColor.darken(.1)
        });
        return katex.render(str, multElt);
      };
    })(this);
    viewT = dynView.view;
    labeled = this.labeledVectors(view, {
      vectors: [vectorIn, vectorOut],
      colors: [vecColor, vecColor.darken(.1)],
      labels: [vecName, matName + vecName],
      live: true,
      zeroPoints: false,
      zeroThreshold: 0.3,
      vectorOpts: {
        zIndex: 4
      },
      labelOpts: {
        zIndex: 5
      },
      zeroOpts: {
        zIndex: 5
      }
    });
    if (size === 2) {
      snap = function() {};
    } else if (size === 3) {
      subspace = this.subspace({
        vectors: [dynView.v1, dynView.v2]
      });
      snapped = new THREE.Vector3();
      diff = new THREE.Vector3();
      snap = function(vec) {
        subspace.project(vec, snapped);
        diff.copy(vec).sub(snapped);
        if (diff.lengthSq() <= 0.01) {
          return vec.copy(snapped);
        }
      };
    }
    drag = this.draggable(view, {
      points: [vectorIn],
      onDrag: snap,
      postDrag: function() {
        computeOut();
        computePath();
        return dragHook(vectorIn);
      }
    });
    computeOut = function() {
      if (!params["Test vector"]) {
        return;
      }
      vectorOut[0] = vectorIn[0], vectorOut[1] = vectorIn[1], vectorOut[2] = vectorIn[2];
      dynView.matrixOrigCoords.applyToVector3Array(vectorOut);
      return updateCaption();
    };
    this.setVec = function(vec) {
      vectorIn[0] = vec[0], vectorIn[1] = vec[1], vectorIn[2] = vec[2];
      computeOut();
      return computePath();
    };
    this.toggleVector = (function(_this) {
      return function(val) {
        drag.enabled = params["Test vector"] || params["Show path"];
        if (val) {
          labeled.show();
          computeOut();
          return multElt.classList.remove("hidden");
        } else {
          labeled.hide();
          return multElt.classList.add("hidden");
        }
      };
    })(this);
    this.toggleVector(params["Test vector"]);
    path = (function() {
      var j, results;
      results = [];
      for (j = 0; j <= 100; j++) {
        results.push([0, 0, 0]);
      }
      return results;
    })();
    computePath = (function(_this) {
      return function() {
        var vec;
        if (!params["Show path"]) {
          return;
        }
        vec = vectorIn.slice();
        dynView.coordMatInv.applyToVector3Array(vec);
        return controller.current.makePath(vec, path);
      };
    })(this);
    viewT.array({
      channels: 3,
      width: path.length,
      live: true,
      data: path
    });
    pathElt = viewT.line({
      color: new Color("orange").arr(),
      width: 5,
      opacity: 1,
      zBias: 0,
      zIndex: 4
    });
    this.togglePath = (function(_this) {
      return function(val) {
        if (val) {
          computePath();
        }
        pathElt.set('visible', val);
        return drag.enabled = params["Test vector"] || params["Show path"];
      };
    })(this);
    return this.togglePath(params["Show path"]);
  });
};

matrix = urlParams.get('mat', 'matrix', [[1 / 2, 1 / 2], [-1 / 2, 1 / 2]]);

matName = (ref1 = urlParams.matname) != null ? ref1 : 'A';

vecName = (ref2 = urlParams.vecname) != null ? ref2 : 'x';

size = urlParams.eigenz != null ? 3 : 2;

eigenz = urlParams.get('eigenz', 'float', 1);

v1 = urlParams.get('v1', 'float[]', [1, 0, 0]);

v2 = urlParams.get('v2', 'float[]', [0, 1, 0]);

v3 = urlParams.get('v3', 'float[]', [0, 0, 1]);

if (v1[2] == null) {
  v1[2] = 0;
}

if (v2[2] == null) {
  v2[2] = 0;
}

if (v3[2] == null) {
  v3[2] = 0;
}

if (urlParams.testmat != null) {
  matrix = testMatrices[urlParams.testmat];
}

duration = urlParams.get('duration', 'float', 2.5);

vectorIn = urlParams.get('y', 'float[]', [5, 0, 0]);

if (vectorIn[2] == null) {
  vectorIn[2] = 0;
}

vectorIn[0] /= 10;

vectorIn[1] /= 10;

vectorIn[2] /= 10;

ref3 = matrixInfo(matrix), basis1 = ref3.basis1, basis2 = ref3.basis2, type = ref3.type, typeOpts = ref3.typeOpts, axisColors = ref3.axisColors, eigenStrs = ref3.eigenStrs;

if (size === 3) {
  eigenStrs.push("This is the <span class=\"espace-3\">" + ((eigenz.toFixed(2)) + "-eigenspace.</span>"));
  typeOpts.scaleZ = eigenz;
}

eigenStr = eigenStrs.join("<br>");

v1p = mv3v2(v1, v2, basis1);

v2p = mv3v2(v1, v2, basis2);

v3p = v3;

controller = new dynamics.Controller({
  continuous: false,
  flow: urlParams.get('flow', 'bool', false),
  duration: duration,
  is3D: size === 3
});

dynView = new dynamics.DynamicsView({
  is3D: size === 3,
  axisOpts: {
    width: size === 3 ? 5 : 3,
    opacity: 1.0
  },
  axisColors: axisColors,
  refColor: new Color("blue").arr(),
  timer: true
});

dynView.setCoords(v1p, v2p, v3p);

controller.addView(dynView);

controller.loadDynamics(type, typeOpts);

params = {
  Multiply: null,
  "Un-multiply": null,
  "Test vector": urlParams.get('vec', 'bool', true),
  "Show path": urlParams.get('path', 'bool', true)
};

window.demo = demo = dynamicsDemo(controller, {
  dynView: dynView,
  params: params,
  matName: matName,
  vecName: vecName,
  size: size,
  eigenz: eigenz,
  eigenStr: eigenStr,
  vectorIn: vectorIn
});

gui = new dat.GUI({
  autoPlace: false
});

params["Multiply"] = controller.step;

gui.add(params, "Multiply");

params["Un-multiply"] = controller.unStep;

gui.add(params, "Un-multiply");

gui.add(params, "Test vector").onFinishChange(demo.toggleVector);

gui.add(params, "Show path").onFinishChange(demo.togglePath);

document.getElementById('gui-container').appendChild(gui.domElement);

if (controller.flow) {
  controller.start(55);
}


        });
    </script>
</body>
</html>

